#!/usr/bin/env ruby
#
#  Created by Paul Gower on 2007-10-24.
#  Copyright (c) 2007. All rights reserved.

require 'sqlite3'

class Jumbalaya
  @@connection = nil
  @@connection_accessor = nil
  @id = nil
  
  def initialize(params={})
    if not @@connection then raise "No Database registered with this Jumbalaya Class, must register a db before using with register_database" end
    if params[:id] then
      @id = params[:id]
    else
      @id = new_record
    end
    c = column_names_as_array - ["id"]
    c.each do |cn|
      if params[cn.to_sym] then
        self.send(cn,params[cn.to_sym])
      end
    end
    # save TODO: uncomment this when the save method actually does something
  end
  
  def self.register_database(type, params={})
    case type
    when :sqlite3
      if not params[:filename] then return end # FIXME: Will die without a decent explanation, make it throw an error
      @@connection = SQLite3::Database.new( params[:filename] )
      @@connection.results_as_hash = true
      @@connection.type_translation = true
      # TODO: along with the accessor methods for all the other databases this needs to be modified to return data in some kind of db indpendant way, maybe Jumbalya::Support or something
      @@connection_accessor =  lambda do |sql|
        begin
          r = @@connection.execute2(sql)
          c = r.shift
          return {:results => r, :columns => c}
        rescue SQLite3::SQLException
          puts "SQLite3, Query failed: \n" + $!
        end
      end

      
      puts "connection set to sqlite3"

    when :mysql # fixme: this is super rough, will most likely not work and will need alot of testing
      if not params[:host] or not params[:username] or not params[:password] or not params[:db] then return end # FIXME: Will die without a decent explanation, make it throw an error
      @@connection = Mysql::new(params[:host], params[:username], params[:password], params[:db])
      @@connection_accessor = lambda do |sql|
        results = @@connection.query(sql)
      end

      
      put "connection set to mysql"
      
    else
      raise "Connection type #{type}, does not exist" #TODO: Create proper error classes for these errors
    end
    @@connection #TODO: returned for debugging purposes, remove later
  end
  
  def id
    @id
  end
  
  private
  
  def exec_sql(sql)
    @@connection_accessor.call(sql)
  end
  
  def self.exec_sql(sql)
    @@connection_accessor.call(sql)
  end
  
  def table_name
    self.class.to_s.downcase
  end

  def self.table_name
    self.name.to_s.downcase
  end
  
  def column_names_as_array
    r = exec_sql("select * from #{table_name}")
    r[:columns]
  end
  
  def method_missing(method_id, *params)
    puts "method_missing: #{method_id.to_s}, passed: #{params.to_s}"
    if column_names_as_array.include?(method_id.to_s) then
      get_value(method_id.to_s)
    elsif regexmatch = /(.*)=$/.match(method_id.to_s) then
      unless regexmatch[1] == "id" then set_value(regexmatch[1],params.first) end
    else
      raise NoMethodError, method_id.to_s
    end
  end
  
  def self.method_missing(method_id, *params)
  # this is a class method, if no adaptor has been setup then we should at least try with the defaults before failing
    puts "self.method_missing: method_id: #{method_id.to_s}, params: #{params.inspect}"
    if not @@connection then raise "No Database registered with this Jumbalaya Class, must register a db before using with register_database" end
    if /^find$/.match(method_id.to_s)
      self.find(params)
    elsif regexmatch = /find_by_(.*)/.match(method_id.to_s)
      self.find_by(regexmatch[1],params.first)
    elsif regexmatch = /find_by_(.*)_and_(.*)/.match(method_id.to_s)
      self.find_by_and(regexmatch[1],params.first,regexmatch[2],params[1])
    else 
      raise NoMethodError, method_id.to_s
    end
  end

  def new_record
    @@connection.results_as_hash = false
    r = exec_sql("select id from #{table_name} order by id desc")
    @@connection.results_as_hash = true
    nid = r[:results].first.first.to_i + 1
    exec_sql("insert into #{table_name} (id) values (#{nid})")
    nid
  end
  
  def self.create_from_record(id)
    self.new(:id => id)
  end
  
  # FIXME: When the id cant be found find dies because the array/hash structure returned is different, this applies for find_by and find_by_and
  def self.find(value)
    if value.kind_of?(Integer) then
      r = exec_sql("select id from #{table_name} where id=\"#{value}\"")
      puts r.inspect
      puts r[:results].inspect
      create_from_record(r[:results].first["id"])
    elsif value.kind_of?(Symbol) then
      case value
      when :first
        r = exec_sql("SELECT id FROM #{table_name} ORDER BY id ASC")
        create_from_record(r[:results].first["id"])
      when :last
        r = exec_sql("SELECT id FROM #{table_name} ORDER BY id DESC")
        create_from_record(r[:results].first["id"])
      end
    else
      return nil
    end
  end
  
  def self.find_by(column,value)
    r = exec_sql("select id from #{table_name} where #{column}=\"#{value}\"")
    create_from_record(r[:results].first["id"])
  end
  
  def self.find_by_and(column_one,value_one,column_two,value_two)
    r = exec_sql("select id from #{table_name} where #{column_one}=\"#{value_one}\" and #{column_two}=\"#{value_two}\"")
    create_from_record(r[:results].first["id"])
  end
  
  def get_value(column)
    r = exec_sql("select #{column} from #{table_name} where id=\"#{@id}\"")
    puts r.inspect
    r[:results].first[column.to_s]
  end
  
  def set_value(column, value)
    exec_sql("update #{table_name} set #{column}=\"#{value}\" where id=\"#{@id}\"")
  end
  
end

class Boxes < Jumbalaya
end